using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Timers;
using AutoTest.Core.Infrastructure.Messaging;
using log4net;

namespace AutoTest.Core.Domain.TestQueuing
{
    public class QueueTestInterceptor : IQueueTestInterceptor, IInterceptorOf<RunTestsMessage>, IConsumerOf<TestRunStartedMessage>,
                                        IConsumerOf<TestRunCompletedMessage>
    {
        private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        private readonly IMessageBus _messageBus;
        private readonly Timer _timer;
        private readonly List<RunTestsMessage> _runTestMesssages = new List<RunTestsMessage>();
        private bool _isCurrentlyRunningTests;

        public QueueTestInterceptor(IMessageBus messageBus)
        {
            _messageBus = messageBus;

            _timer = new Timer
                         {
                             AutoReset = false,
                             Interval = TimeSpan.FromSeconds(0.5).TotalMilliseconds
                         };
            _timer.Elapsed += TimerOnElapsed;
        }

        public void Intercept(RunTestsMessage message)
        {
            Logger.DebugFormat("Queuing {0}", string.Join(",", message.Tests.Select(t => t.Name).ToArray()));
            _runTestMesssages.Add(message);
            ResetTimer();
        }

        private void ResetTimer()
        {
            _timer.Stop();
            _timer.Start();
        }

        private void TimerOnElapsed(object sender, ElapsedEventArgs e)
        {
            if (_isCurrentlyRunningTests)
            {
                Logger.Debug("Waiting for current test run to complete, before starting next test run...");
                ResetTimer();
                return;
            }

            var testNames = _runTestMesssages.SelectMany(msg => msg.Tests).Distinct().ToList();
            _runTestMesssages.Clear();
            _messageBus.Publish(this, new RunTestsMessage(testNames));
        }

        public void Consume(TestRunStartedMessage t)
        {
            _isCurrentlyRunningTests = true;
        }

        public void Consume(TestRunCompletedMessage t)
        {
            _isCurrentlyRunningTests = false;
        }
    }
}